package ua.kharkov.khpi.tarasenko.bach.core;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import ua.kharkov.khpi.tarasenko.bach.core.db.ConnectionManager;
import ua.kharkov.khpi.tarasenko.bach.core.db.DBUtils;
import ua.kharkov.khpi.tarasenko.bach.core.item.CategoryContainer;
import ua.kharkov.khpi.tarasenko.bach.dao.core.DAOFactory;
import ua.kharkov.khpi.tarasenko.bach.dao.core.category.CategoryDAO;
import ua.kharkov.khpi.tarasenko.bach.dao.core.category.CategoryDTO;

/**
 * Application context listener.
 */
public class LifeCycleListener implements ServletContextListener {
	private static final Logger log = Logger.getLogger(LifeCycleListener.class);

	private static final String COMMAND_MANAGER_FULL_CLASS_NAME = "ua.kharkov.khpi.tarasenko.bach.web.command.CommandManager";
	private static ServletContext context;

	@Override
	public void contextDestroyed(ServletContextEvent event) {
		log.info("Summary task 4 stopped!");
	}

	@Override
	public void contextInitialized(ServletContextEvent event) {
		log("Servlet context initialization started!");
		context = event.getServletContext();
		initLog4j(context);
		initCommandManager();
		initConnectionManager();
		initItemCategoriesContainer();
		initI18n();

		if (log.isDebugEnabled()) {
			log.debug("Servlet context initialization finished!");
		}
		log.info("Summary task 4 started!");
	}

	/**
	 * Initializes log4j.
	 * 
	 * @param context
	 *            - servlet context.
	 */
	private void initLog4j(ServletContext context) {
		PropertyConfigurator.configure(context
				.getRealPath("WEB-INF/log4j.properties"));
		if (log.isDebugEnabled()) {
			log.debug("Log4j initialization finished!");
		}
	}

	/**
	 * Initializing command manager.
	 * 
	 */
	private void initCommandManager() {
		try {
			Class.forName(COMMAND_MANAGER_FULL_CLASS_NAME);
		} catch (ClassNotFoundException e) {
			log.fatal("Can't load " + COMMAND_MANAGER_FULL_CLASS_NAME
					+ " class!", e);

			// Can't continue without that class so throwing RuntimeException
			throw new RuntimeException(e);
		}

		if (log.isDebugEnabled()) {
			log.debug("Command Manager initialized.");
		}
	}

	/**
	 * Initializing connection manager
	 * 
	 */
	private void initConnectionManager() {
		Context initContext;
		try {
			initContext = new InitialContext();
			DataSource ds = (DataSource) (initContext
					.lookup("java:comp/env/jdbc/e_shopDB"));
			ConnectionManager.init(ds);
		} catch (Exception e) {
			log.fatal("Can't initialize Connection Manager.", e);
			throw new RuntimeException(e);
		}

		if (log.isDebugEnabled()) {
			log.debug("Connection pool initialized.");
		}
	}

	/**
	 * Initializing item's categories container.
	 * 
	 */
	private void initItemCategoriesContainer() {
		Connection conn = null;
		try {
			conn = ConnectionManager.getConnection();
			CategoryDAO categoryDAO = DAOFactory.getDAOFactory(
					DAOFactory.DAO_TYPE.MYSQL).getCategoryDAO(conn);
			List<CategoryDTO> categories = categoryDAO.findAllCategories();
			if (categories.isEmpty()) {
				log.warn("Categories list is empty!");
			} else {
				for (CategoryDTO cat : categories) {
					CategoryContainer.addCategory(cat.getId(), cat.getName());
				}
				if (log.isDebugEnabled()) {
					log.debug("Categories container initialized, added "
							+ categories.size() + " categories");
				}

				Map<Integer, String> categoriesMap = CategoryContainer
						.getCategoryMap();
				context.setAttribute(Constant.ATTRIBUTE_CATEGORY_CONTAINER,
						categoriesMap);
				if (log.isDebugEnabled()) {
					log.debug("Set application attribute "
							+ Constant.ATTRIBUTE_CATEGORY_CONTAINER + "="
							+ categoriesMap);
				}
			}
		} catch (EShopException e) {
			Throwable thr = null;
			if (ExceptionUtils.getRootCause(e) != null) {
				thr = ExceptionUtils.getRootCause(e);
			} else {

				// Exception doesn't have nested exception.
				thr = e;
			}

			log.fatal("Can not initialize Categories!\n", thr);
			throw new RuntimeException(thr);
		} finally {
			if (conn != null) {
				DBUtils.close(conn);
			}
		}
	}

	/**
	 * Initialize I18n subsystem.
	 * 
	 */
	private void initI18n() {
		String availableLocales = context.getInitParameter("locals");
		if ((availableLocales == null) || (availableLocales.isEmpty())) {
			log.warn("'locals' initialization parameter is empty. Default locale will be used!");
		} else {
			List<String> locals = new ArrayList<String>();
			StringTokenizer tokenizer = new StringTokenizer(availableLocales);
			while (tokenizer.hasMoreTokens()) {
				locals.add(tokenizer.nextToken());
			}

			context.setAttribute(Constant.ATTRIBUTE_AVAILABLE_LOCALS, locals);
			if (log.isDebugEnabled()) {
				log.debug("Set application attribute "
						+ Constant.ATTRIBUTE_AVAILABLE_LOCALS + "=" + locals);
			}
		}

		// Initialize LocalizedResourceManager
		String resourcesFileName = context
				.getInitParameter("javax.servlet.jsp.jstl.fmt.localizationContext");
		if ((resourcesFileName != null) && (!resourcesFileName.isEmpty())) {

			LocalizedResourceManager.init(resourcesFileName);
			if (log.isTraceEnabled()) {
				log.trace("LocalizedResourceManager initialized with resourcesFileName="
						+ resourcesFileName);
			}

		} else {
			log.fatal("Resorces file is not specified!");
			throw new RuntimeException("Resorces file is not specified!");
		}

		if (log.isDebugEnabled()) {
			log.debug("I18n subsystem initialized.");
		}
	}

	/**
	 * Use this logger before initializing log4j.
	 * 
	 * @param message
	 */
	private void log(String message) {
		System.out.println("[LifeCycleListener] " + message);
	}
}
